package cn.xuguowen.util;

import cn.xuguowen.exception.RedisShareLockException;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.Validate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

/**
 * ClassName: RedisShareLockUtil
 * Package: cn.xuguowen.util
 * Description:
 *      redis实现分布式锁
 * @Author 徐国文
 * @Create 2023/3/17 15:38
 * @Version 1.0
 */
@Component
public class RedisShareLockUtil {

    @Autowired
    private RedisUtil redisUtil;

    public static final long TIME_OUT_TIME = 1000L;
    /**
     * 加锁
     * @param key
     * @param requestId     业务标识，和其他业务的锁区分开
     * @param time          time时间后释放锁资源，避免死锁
     * @return
     */
    public boolean lock(String key,String requestId,Long time) {
        // 1.应该对参数进行检验
        if (StringUtils.isBlank(key) || StringUtils.isBlank(requestId) || time <= 0) {
            throw new RedisShareLockException("分布式锁-加锁参数异常");
        }

        // 加锁成功或者失败的标志
        boolean result = false;

        // 2.开始加锁的操作，锁是可以自旋的
        long currentTime = System.currentTimeMillis();
        long timeOutTime = currentTime + TIME_OUT_TIME;
        while (currentTime < timeOutTime) {
            // 加锁
            result = redisUtil.setNx(key, requestId, time, TimeUnit.MILLISECONDS);
            if (result) {
                return true;        // 加锁成功
            }

            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

            // 程序走到这里表示加锁失败了，开始锁自旋
            currentTime = System.currentTimeMillis();
        }
        return result;
    }

    /**
     * 释放所资源
     * @param key       根据key释放锁资源
     * @param requestId 业务value，释放指定的锁资源
     * @return
     */
    public boolean unLock(String key,String requestId) {
        // 参数的校验
        if (StringUtils.isBlank(key) || StringUtils.isBlank(requestId)) {
            throw new RedisShareLockException("分布式锁-释放锁参数异常");
        }

        // 1.先根据key查询
        try {
            String value = redisUtil.get(key);
            if (StringUtils.isNotBlank(value) && requestId.equals(value)) {
                redisUtil.del(key);
                return true;
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        return false;
    }

    /**
     * 再次尝试加锁
     * @param key
     * @param requestId     业务标识，和其他业务的锁区分开
     * @param time          time时间后释放锁资源，避免死锁
     */
    public boolean tryLock(String key,String requestId,Long time) {
        // 1.应该对参数进行检验
        if (StringUtils.isBlank(key) || StringUtils.isBlank(requestId) || time <= 0) {
            throw new RedisShareLockException("分布式锁-加锁参数异常");
        }

        // 加锁
        return redisUtil.setNx(key, requestId, time, TimeUnit.MILLISECONDS);

    }
}
